<?php
namespace app\common\service;

use think\exception\HttpException;
use think\facade\Config;
use plug\SHttp;
use think\cache\driver\Redis;

class Wechat
{

    /**
     * 获取access_token
     * @return void
     * @throws \think\Exception
     */
    public static function GetAccessToken(){
        $wxconf = config::pull('weixin');
        $redisName = $wxconf['AppID'].'AuthorizeTokenInfo';
        $Redis = new Redis();

        //查询缓存AccessToken是否已过期
        $AuthorizeTokenInfo = json_decode($Redis->get($redisName),true);
        if($AuthorizeTokenInfo && @$AuthorizeTokenInfo['access_token_exp'] > time()){
            return $AuthorizeTokenInfo['access_token'];
        }

        $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=".$wxconf['AppID']."&secret=".$wxconf['AppSecret'];
        $return = SHttp::http_curl($url);
        $data = json_decode($return['str'],true);
        if(!$data || !empty($data['errcode']) || !empty($data['errmsg'])){
            throw new \think\Exception($data['errmsg'], $data['errcode']);
        }

        //存redis里面
        $data['access_token_exp'] = calculate([time(),7000],'+',0);
        $Redis->set($redisName,json_encode($data));
        return $data['access_token'];
    }

    /**
     * 获取通用access_token
     * @return void
     * @throws \think\Exception
     */
    public static function GetNormalAccessToken($wxconf){
        $redisName = $wxconf['AppID'].'AuthorizeTokenInfo';
        $Redis = new Redis();

        //查询缓存AccessToken是否已过期
        $AuthorizeTokenInfo = json_decode($Redis->get($redisName),true);
        if($AuthorizeTokenInfo && @$AuthorizeTokenInfo['access_token_exp'] > time()){
            return $AuthorizeTokenInfo['access_token'];
        }

        $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=".$wxconf['AppID']."&secret=".$wxconf['AppSecret'];
        $return = SHttp::http_curl($url);
        $data = json_decode($return['str'],true);
        if(!$data || !empty($data['errcode']) || !empty($data['errmsg'])){
            throw new \think\Exception($data['errmsg'], $data['errcode']);
        }

        //存redis里面
        $data['access_token_exp'] = calculate([time(),7000],'+',0);
        $Redis->set($redisName,json_encode($data));
        return $data['access_token'];
    }

    /**
     * @title 获取用户小程序码
     * @param string $page 默认是主页，页面 page，例如 pages/index/index，根路径前不要填加 /，不能携带参数（参数请放在 scene 字段里），如果不填写这个字段，默认跳主页面
     * @param int $width 默认430，二维码的宽度，单位 px，最小 280px，最大 1280px
     * @param bool $auto_color 自动配置线条颜色，如果颜色依然是黑色，则说明不建议配置主色调，默认 false
     * @param string $line_color 默认是{"r":0,"g":0,"b":0} 。auto_color 为 false 时生效，使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} 十进制表示
     * @param bool $is_hyaline 默认是false，是否需要透明底色，为 true 时，生成透明底色的小程序
     * @return bool
     */
    public static function getwxacodeunlimit($config,$name,$arr,$path = 'QRcode/',$page='pages/index/index',$width=430,$auto_color=false,$line_color='{"r":0,"g":0,"b":0}',$is_hyaline=false)
    {
        //获取access_token
        $access_token = Wechat::GetNormalAccessToken($config);
        $url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=".$access_token;
        $data = [
            'scene' => http_build_query($arr),
            'page' => $page,
            'width' => $width,
            'auto_color' => $auto_color,
            'line_color' => json_decode($line_color),
            'is_hyaline' => $is_hyaline,
        ];

        $return = SHttp::http_curl($url,json_encode($data),[],'post');
        $data = json_decode($return['str'],true);
        if ($data['errcode']) {
            throw new \think\Exception($data['errmsg'].',错误码：'. $data['errcode']);
        }

        $dirname = 'uploads/'.$path;
        $query['path'] = $dirname;
        if (!file_exists($query['path'])) {
            if (!mkdir($query['path'], 0777, true)) {
                throw new \think\Exception('创建文件夹失败', -1);
            }
        }

        $query['name'] = $name.'.jpg';
        $fullName = $query['path'] . $query['name'];
        if (!file_put_contents($fullName,$return)) {
            throw new \think\Exception('上传错误', -1);
        }

        return $fullName;
    }

    /**
     * @title 小程序消息推送
    * @param int $touser 接受推送用户id
     * @param string $page 默认是主页，页面 page，例如 pages/index/index，根路径前不要填加 /，支持带参数,（示例index?foo=bar）。该字段不填则模板无跳转
    * @param string $data 模板内容，格式形如 { "key1": { "value": any }, "key2": { "value": any } }的object
    * @param string $template_id 所需下发的订阅模板id
    * @param string $miniprogram_state 跳转小程序类型：developer为开发版；trial为体验版；formal为正式版；默认为正式版
    * @param string $lang 进入小程序查看”的语言类型，支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文)，默认为zh_CN
    * @return bool
        */
    public static function messagepush($config,$touser,$template_id,$page = '',$post_data,$miniprogram_state = 'formal',$lang = 'zh_CN')
    {
//        var_dump(json_encode($post_data,true));die;
        //获取access_token
        $access_token = Wechat::GetNormalAccessToken($config);
        $url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=".$access_token;
        $data = [
            'access_token' => $access_token,
            'template_id' => $template_id, // 模板id
            'page' => $page,
            'touser' => $touser,
            'data' => $post_data,
            'miniprogram_state' => $miniprogram_state,
            'lang' => $lang,
        ];

        $return = SHttp::http_curl($url,json_encode($data),[],'post');
        $data = json_decode($return['str'],true);
        if ($data['errcode']) {
            throw new \think\Exception($data['errmsg'].',错误码：'. $data['errcode']);
        }

        return $data;
    }

    /**
     * 获取公众号授权链接
     * @return string[]
     */
    public static function GetAuthorizeUrl($redirect_uri){
        $wxconf = config::pull('weixin');
        $url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=".$wxconf['AppID']."&redirect_uri=".$redirect_uri."&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";
        return ['url'=>$url];
    }

    /**
     * 通过 code 换取网页授权access_token和openid
     * 这里通过 code 换取的是一个特殊的网页授权access_token,与基础支持中的access_token（该access_token用于调用其他接口）不同
     * @param $code
     * @return false|mixed
     */
    public static function AuthorizeInfo($code){
        $wxconf = config::pull('weixin');
        $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=".$wxconf['AppID']."&secret=".$wxconf['AppSecret']."&code=".$code."&grant_type=authorization_code";
        $return = SHttp::http_curl($url);
        $data = json_decode($return['str'],true);
        if(!empty($data['errcode']) || !empty($data['errmsg'])){
            throw new HttpException(-2,$data['errmsg'].$data['errcode']);
        }
        return $data;
    }

    /**
     * 获取用户基本信息（包括UnionID机制）
     * @param $access_token
     * @param $openid
     * @return false|mixed
     */
    public static function userinfo($access_token,$openid){
        $url = "https://api.weixin.qq.com/sns/userinfo?access_token=".$access_token."&openid=".$openid."&lang=zh_CN";
        $return = SHttp::http_curl($url);
        $data = json_decode($return['str'],true);
        if(!empty($data['errcode']) || !empty($data['errmsg'])){
            throw new HttpException(-2,$data['errmsg'].$data['errcode']);
        }
        return $data;
    }

    /**
     * 生成签名
     * @return 签名，本函数不覆盖sign成员变量，如要设置签名需要调用SetSign方法赋值
     */
    public static function MakeSign($data)
    {
        //签名步骤一：按字典序排序参数
        ksort($data);
        //格式化参数格式化成url参数
        $string = ToUrlParams();
        //签名步骤二：在string后加入KEY
        $string = $string . "&key=".config::pull('weixin')['AppSecret'];
        //签名步骤三：MD5加密
        $string = md5($string);
        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

    /**
     * 获取授权页ticket
     */
    public static function JsapiTicket(){
        $wxconf = config::pull('weixin');
        $redisName = $wxconf['AppID'].'AuthorizeTicketInfo';
        $Redis = new Redis();

        //查询缓存ticket是否已过期
        $AuthorizeTicketInfo = json_decode($Redis->get($redisName),true);
        if($AuthorizeTicketInfo && @$AuthorizeTicketInfo['ticket_exp'] > time()){
            return $AuthorizeTicketInfo['ticket'];
        }

        //获取access_token
        $access_token = Wechat::GetAccessToken();
        $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=".$access_token;
        $return = SHttp::http_curl($url);
        $data = json_decode($return['str'],true);
        if(!$data || empty($data['ticket']) || empty($data['expires_in'])){
            throw new \think\Exception($data['errmsg'], $data['errcode']);
        }

        //存redis里面
        $data['ticket_exp'] = calculate([time(),7000],'+',0);
        $Redis->set($redisName,json_encode($data));
        return $data['ticket'];
    }

    /**
     * 小程序获取openid
     * 登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。更多使用方法详见小程序登录。
     * @param $code
     * @return void
     * @throws \think\Exception
     */
    public static function jscode2session($code)
    {
        $wxconf = config::pull('weixin_applet');
        $url = "https://api.weixin.qq.com/sns/jscode2session?appid=" . $wxconf['AppID'] . "&secret=" . $wxconf['AppSecret'] . "&js_code=" . $code . "&grant_type=authorization_code";
        $return = SHttp::http_curl($url);
        $data = json_decode($return['str'],true);

        if(!$data || empty($data['session_key']) || empty($data['openid'])){
            throw new \think\Exception($data['errmsg'], $data['errcode']);
        }
        return $data;
    }

    /**
     * 检验数据的真实性，并且获取解密后的明文.https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html
     * @param $session_key string 会话密钥
     * @param $encryptedData string 加密的用户数据
     * @param $iv string 与用户数据一同返回的初始向量
     * @param $data string 解密后的原文
     *
     * @return int 成功0，失败返回对应的错误码
     */
    public static function decryptData($session_key,$encryptedData, $iv, &$data )
    {
        $wxconf = config::pull('weixin_applet');
        if (strlen($session_key) != 24) {
            return -41001;
        }
        $aesKey=base64_decode($session_key);


        if (strlen($iv) != 24) {
            return -41002;
        }
        $aesIV=base64_decode($iv);

        $aesCipher=base64_decode($encryptedData);

        $result=openssl_decrypt( $aesCipher, "AES-128-CBC", $aesKey, 1, $aesIV);

        $dataObj=json_decode( $result );
        if( $dataObj  == NULL )
        {
            return -410031;
        }
        if( $dataObj->watermark->appid != $wxconf['AppID'] )
        {
            return -410032;
        }
        $data = $result;
        return 0;
    }


}
